package com.yonyou.ucf.mdf.app.mobile.support;


import com.yonyou.ucf.mdf.app.mobile.support.trd.DigestUtil;
import org.apache.commons.lang3.ArrayUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * @author
 */
public class AESCipher {

    private static final byte[] KEY_GEN_SALT = "œ∑®†¥øπ“‘«".getBytes();

    private static final String IV_STRING = "BEAUTY AND BEAST";

    private static final Charset UTF_8 = StandardCharsets.UTF_8;

    private AESCipher() {

    }

    private static byte[] build16Bytes(byte[] source) {

        if (source == null) {
            throw new ECConfigurationException("AES Key Can not be null.");
        }

        if (source.length == 16) {
            return source;
        }

        byte[] bytes = DigestUtil.signByMD5(source);

        byte[] finalBytes = ArrayUtils.addAll(bytes, KEY_GEN_SALT);

        byte[] result = DigestUtil.signBySHA512(finalBytes);

        return ArrayUtils.subarray(result, 0, 16);
    }

    public static String encrypt(String content, String key) {
        byte[] contentBytes = content.getBytes(UTF_8);
        byte[] keyBytes = key.getBytes(UTF_8);
        byte[] encryptedBytes = aesEncryptBytes(contentBytes, keyBytes);
        return Base64.getUrlEncoder().encodeToString(encryptedBytes);
    }

    public static String decrypt(String content, String key) {
        byte[] encryptedBytes;
        try {
            encryptedBytes = Base64.getUrlDecoder().decode(content);
        } catch (IllegalArgumentException e) {
            try {
                encryptedBytes = Base64.getDecoder().decode(content);
            } catch (IllegalArgumentException e1) {
                throw new ECAuthenticationFailedException("Token Decrypt Failed, Invalid", e1);
            }
        }

        byte[] keyBytes = key.getBytes(UTF_8);
        byte[] decryptedBytes = aesDecryptBytes(encryptedBytes, keyBytes);
        return new String(decryptedBytes, UTF_8);
    }

    public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) {
        return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
    }

    public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) {
        return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
    }

    private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes,
                                          int mode) {

        byte[] realKeyBytes = keyBytes;

        //AES要求必须为指定位数的KEY,8,16,32(32要求JCE文件)
        if (keyBytes.length != 16) {
            realKeyBytes = build16Bytes(realKeyBytes);
        }

        SecretKeySpec secretKey = new SecretKeySpec(realKeyBytes, "AES");

        byte[] initParam = IV_STRING.getBytes(UTF_8);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);

        Cipher cipher;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new ECConfigurationException("AES Encrypt or Decrypt Failed when get cipher instance", e);
        }

        try {
            cipher.init(mode, secretKey, ivParameterSpec);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new ECConfigurationException("AES Encrypt or Decrypt Failed when init cipher", e);
        }

        try {
            return cipher.doFinal(contentBytes);
        } catch (IllegalBlockSizeException e) {
            throw new ECAuthenticationFailedException("AES Encrypt or Decrypt Failed when doFinal", e);
        } catch (BadPaddingException e) {
            throw new ECAuthenticationFailedException("Token Decrypt Failed", e);
        }
    }

}